33.RAF 和 RIC 是什么
requestAnimationFrame:
它既不是微任务也不属于宏任务 告诉浏览器在下次重绘之前执行传入的回调函数(通常是操纵 dom,更新动画的函数);由于是每帧执行一次,那结果就是每秒的执行次数与浏览器屏幕刷新次数一样,通常是每秒 60 次。
js
let frame = 0;
let lastTime = Date.now();
const timerange = 10000; // FPS统计打点时间间隔
let rAFID = 0;
const performance = () => {
frame++;
const now = Date.now();
if (now - lastTime > timerange) {
const fps = Math.round((frame * 1000) / (now - lastTime));
console.log(`[rAF] 10s内Frames: ${frame}, FPS:${fps}`);
frame = 0;
lastTime = now;
}
rAFID = rAF();
};
export const rAF = () => (rAFID = requestAnimationFrame(performance));
export const cAF = () => cancelAnimationFrame(rAFID);
requestIdleCallback
会在浏览器空闲时间执行回调,也就是允许开发人员在主事件循环中执行低优先级任务,而不影响一些延迟关键事件。如果有多个回调,会按照先进先出原则执行,但是当传入了 timeout,为了避免超时,有可能会打乱这个顺序
它是:实验 API, 不重要优先级不高 每帧渲染后还有时间,则可以执行
可以做:数据上报
js
requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
function myNonEssentialWork(deadline) {
// 当回调函数是由于超时才得以执行的话,deadline.didTimeout为true 用户就有可能感受到卡顿, 因为一帧的时间已经超过 16ms 了.
// 核心是一个循环,只要有剩余时间,或者已经到到 timeout 期限,且列表中有任务就会一直持续
while (
(deadline.timeRemaining() > 0 || deadline.didTimeout) &&
tasks.length > 0
) {
doWorkIfNeeded();
}
if (tasks.length > 0) {
taskHandle = requestIdleCallback(myNonEssentialWork);
} else {
taskHandle = 0;
}
}
不支持模拟实现
js
const MyRequestIdleCallback = (callback) => {
let start = Date.now();
const timer = setTimeout(() => {
callback({
didTimeout: false,
timeRemaining: () => {
return Math.max(0, 50 - (Date.now() - start));
},
});
});
return timer;
};
window.requestIdleCallback =
window.requestIdleCallback || MyRequestIdleCallback;
加载 资源
js
export const idleLoadResource = (files: string[], loadMethod: LoadMethod) => {
let asyncLoadSuccess = true;
const tasks = (dealline: DealLine) => {
const length = files.length;
if (dealline.timeRemaining() > idleTime && length > 0 && asyncLoadSuccess) {
console.log(
"[idle use]: 空闲加载资源, 空闲时间为: ",
dealline.timeRemaining()
);
// load one task
const file = files.pop();
if (file) {
asyncLoadSuccess = false;
loadMethod(file)
.then((res: any) => {
asyncLoadSuccess = true;
})
.catch((err: any) => {
console.log(
`[idle use]: 加载错误,取消该资源的预加载 ${file}`,
JSON.stringify(err)
);
asyncLoadSuccess = true;
});
}
}
if (length > 0) {
requestIdleCallback(tasks);
}
};
requestIdleCallback(tasks);
};
js
idleLoadResource(["not-answer", "keep-trying"], (file) => {
const show = import(
/* webpackChunkName: "lottie-[request]-show" */
`./assets/lottie-path/${file}/show.json`
);
const loop = import(
/* webpackChunkName: "lottie-[request]-loop" */
`./assets/lottie-path/${file}/loop.json`
);
return Promise.all([show, loop]);
});